package com.generator.core;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.generator.bean.ApplicationStarterProcessBean;
import com.generator.bean.ApplicationYmlProcessBean;
import com.generator.bean.ControllerProcessBean;
import com.generator.bean.EntityProcessBean;
import com.generator.bean.MapperProcessBean;
import com.generator.bean.MybatisProcessBean;
import com.generator.bean.PomProcessBean;
import com.generator.bean.ServiceProcessBean;
import com.generator.bean.base.GenaratorBaseBean;
import com.generator.bean.common.ClassInfo;
import com.generator.bean.common.Field;
import com.generator.bean.core.GenaratorCtxBean;
import com.generator.config.ContextConfig;
import com.generator.tools.GZIPUtils;
import com.generator.tools.text.StringUtils;
import com.generator.utils.FreemarkerTools;

import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ProcessCoreBiz {
	private FreemarkerTools freemarkerTool;
	private ContextConfig contextConfig;

	public FreemarkerTools getFreemarkerTool() {
		return freemarkerTool;
	}

	public void setFreemarkerTool(FreemarkerTools freemarkerTool) {
		this.freemarkerTool = freemarkerTool;
	}

	public ContextConfig getContextConfig() {
		return contextConfig;
	}

	public void setContextConfig(ContextConfig contextConfig) {
		this.contextConfig = contextConfig;
	}

	public void process() {
		String basePackage = contextConfig.getBasePackage();
		String author = contextConfig.getAuthor();
		String driverClassName = contextConfig.getDriverClassName();
		String url = contextConfig.getUrl();
		String userName = contextConfig.getUser();
		String password = contextConfig.getPassword();

		log.info("driverClassName：{}", driverClassName);
		log.info("url:{}", url);
		log.info("userName:{}", userName);
		log.info("password:{}", password);

		log.info("basePackage:{}", basePackage);
		log.info("author:{}", author);
		Map<String, String> genMap = contextConfig.popularTableEntityMappingsToMap();
		if (genMap.isEmpty()) {
			throw new RuntimeException("请配置正确的tableEntityMappings");
		}
		Connection conn = null;
		// 表查询结果集
		ResultSet tableRS = null;
		// 获取列的结果集
		ResultSet columnRS=null;
		try {
			Class.forName(driverClassName);
			conn = DriverManager.getConnection(url, userName, password);
			DatabaseMetaData dbMetaData = conn.getMetaData();
			String catalog = conn.getCatalog(); // catalog 其实也就是数据库名
			// 获取表的结果集
			tableRS = dbMetaData.getTables(catalog, "%", "%", new String[] { "TABLE" });
			ClassInfo classInfo = null;
			boolean importDdate = false;
			while (tableRS.next()) {
				String tableName = tableRS.getString("TABLE_NAME");
				log.info("tableName:{}", tableName);
				log.info("开始生成表{}对应代码:", tableName);
				classInfo = new ClassInfo();
				classInfo.setTableName(tableName);
				classInfo.setClassComment(tableRS.getString("REMARKS"));
				classInfo.setClassName(genMap.get(tableName));
				classInfo.setFullClassName(contextConfig.getBasePackage() + ".entity." + classInfo.getClassName());
				columnRS = dbMetaData.getColumns(catalog, "%", tableName, "%");

				List<Field> fieldList = new ArrayList<Field>();
				while (columnRS.next()) {
					String columnName = columnRS.getString("COLUMN_NAME");
					String columnType = columnRS.getString("TYPE_NAME").toUpperCase();
					String columnRemark = columnRS.getString("REMARKS");

					if (columnName == null || "".equals(columnName)) {
						continue;
					}

					Field ci = new Field();
					ci.setColumnName(columnName);
					ci.setFieldName(StringUtils.toCamelCase(columnName));
					ci.setFieldComment(columnRemark);
					ci.setFieldClass(StringUtils.dbType2Java(columnType));
					if (ci.getFieldClass().equals("Date")) {
						importDdate = true;
					}
					fieldList.add(ci);
				}
				classInfo.setFieldList(fieldList);
				classInfo.setPrimaryKey(primaryKeyColumnName(dbMetaData, tableName, catalog));
				classInfo.setConversionPrimaryKey(StringUtils.toCamelCase(classInfo.getPrimaryKey()));

				GenaratorCtxBean genaratorCtxBean = new GenaratorCtxBean();
                EntityProcessBean entity = new EntityProcessBean(author, basePackage, classInfo);
				entity.setImportDdate(importDdate);
				genaratorCtxBean.setEntity(entity);
				genaratorCtxBean.setService(new ServiceProcessBean(author, basePackage, classInfo));
				genaratorCtxBean.setMapper(new MapperProcessBean(author, basePackage, classInfo));
				genaratorCtxBean.setMybatis(new MybatisProcessBean(author, basePackage, classInfo));
				genaratorCtxBean.setController(new ControllerProcessBean(author, basePackage, classInfo));

				if (contextConfig.isModelFlag()) {
					EntityProcessBean entityProcessBean = genaratorCtxBean.getEntity();
					genFile(freemarkerTool, contextConfig,
							entityProcessBean.getClassInfo().getClassName().concat(".java"), "entity",
							genaratorCtxBean.getEntity());
				}
				if (contextConfig.isServiceFlag()) {
					EntityProcessBean entityProcessBean = genaratorCtxBean.getEntity();
					genFile(freemarkerTool, contextConfig,
							"I".concat(entityProcessBean.getClassInfo().getClassName()).concat("Service.java"),
							"service", genaratorCtxBean.getService());
				}
				if (contextConfig.isServiceImplFlag()) {
					EntityProcessBean entityProcessBean = genaratorCtxBean.getEntity();
					genFile(freemarkerTool, contextConfig,
							entityProcessBean.getClassInfo().getClassName().concat("ServiceImpl.java"), "service_impl",
							genaratorCtxBean.getService());
				}
				if (contextConfig.isMapperFlag()) {
					EntityProcessBean entityProcessBean = genaratorCtxBean.getEntity();
					genFile(freemarkerTool, contextConfig,
							entityProcessBean.getClassInfo().getClassName().concat("Mapper.java"), "mapper",
							genaratorCtxBean.getMapper());
				}
				if (contextConfig.isMapperXmlFlag()) {
					EntityProcessBean entityProcessBean = genaratorCtxBean.getEntity();
					genFile(freemarkerTool, contextConfig,
							entityProcessBean.getClassInfo().getClassName().concat("Mapper.xml"), "mapper_xml",
							genaratorCtxBean.getMybatis());
				}
				if (contextConfig.isControlFlag()) {
					EntityProcessBean entityProcessBean = genaratorCtxBean.getEntity();
					genFile(freemarkerTool, contextConfig,
							entityProcessBean.getClassInfo().getClassName().concat("Controller.java"), "controller",
							genaratorCtxBean.getController());
				}
				log.info("表{}生成代码完成", tableName);
				columnRS.close();
			}
			PomProcessBean pom = new PomProcessBean(author, basePackage, classInfo, contextConfig);
			genFile(freemarkerTool, contextConfig, "pom.xml", "pom", pom);
			ApplicationYmlProcessBean yml = new ApplicationYmlProcessBean(author, basePackage, classInfo, contextConfig);
			genFile(freemarkerTool, contextConfig, "application.yml", "application_yml", yml);
			ApplicationStarterProcessBean starter = new ApplicationStarterProcessBean(author, basePackage, classInfo, contextConfig);
			genFile(freemarkerTool, contextConfig, StringUtils
					.upperCaseFirst(starter.getContextConfig().getProjectName()).concat("Application.java"),
					"application_starter", starter);
			String diskPath = contextConfig.getCodeDirPath().concat("/").concat(contextConfig.getProjectName());
			try {
				GZIPUtils.zipFile(new File(diskPath));
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("project generate finished!!see it at path:" + diskPath);
		} catch (Exception e) {
			e.printStackTrace();
			log.error("生成项目信息异常。。", e);
		} finally {
			try {
				tableRS.close();
				columnRS.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	private void genFile(FreemarkerTools freemarkerTools, ContextConfig contextConfig, String fileName,
			String modelType, GenaratorBaseBean genaratorCtxBean)
			throws FileNotFoundException, IOException, TemplateException {
		String diskPath = null;
		if (StringUtils.isBlank(contextConfig.getCodeDirPath())) {
			diskPath = System.getProperty("user.dir");
		} else {
			diskPath = contextConfig.getCodeDirPath().concat("/").concat(contextConfig.getProjectName());
		}
		String diskPackagePath = diskPath.concat("/").concat(contextConfig.getCodePath()).concat("/")
				.concat(contextConfig.getBasePackage().replaceAll("\\.", "/"));
		String projectDiskPackagePath = diskPath;
		if ("service_impl".equals(modelType)) {
			diskPackagePath = diskPackagePath.concat("/service/impl");
		} else if ("mapper_xml".equals(modelType)) {
			diskPackagePath = diskPath.concat("/").concat(contextConfig.getMapperPath());
		} else if ("pom".equals(modelType)) {
			diskPackagePath = projectDiskPackagePath.concat("/");
		} else if ("application_yml".equals(modelType)) {
			diskPackagePath = projectDiskPackagePath.concat("/src/main/resources/");
		} else if ("application_starter".equals(modelType)) {
		} else {
			diskPackagePath = diskPackagePath.concat("/").concat(modelType);
		}
		File file = new File(diskPackagePath, fileName);
		if (file.exists()) {
			file.delete();
		} else {
			File dirFile = new File(diskPackagePath);
			if (!dirFile.exists()) {
				dirFile.mkdirs();
			}
		}
		Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
		String src = freemarkerTools.processString("".concat(modelType).concat(".ftl"), genaratorCtxBean);
		out.write(src);
		out.close();
		if("pom".equals(modelType)||
				"application_yml".equals(modelType)||
				"application_starter".equals(modelType)) {
			System.out.println("项目【"+contextConfig.getProjectName()+"】:" + fileName + " generate success!");
		}else {
			System.out.println("模块【"+genaratorCtxBean.getClassInfo().getClassName()+"】（"+modelType+"）:" + fileName + " generate success!");
		}
	}

	private String primaryKeyColumnName(DatabaseMetaData dbMetaData, String tableName, String catalog) {
		try {
			ResultSet primaryKeyResultSet = dbMetaData.getPrimaryKeys(catalog, null, tableName);
			String primaryKeyColumnName = "id";
			while (primaryKeyResultSet.next()) {
				primaryKeyColumnName = primaryKeyResultSet.getString("COLUMN_NAME");
			}
			return primaryKeyColumnName;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
